package com.example.filter;

import com.alibaba.fastjson.JSON;
import com.example.constant.RedisConstance;
import com.example.vo.LoginUser;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.example.config.SecurityConfig;
import com.example.constant.SecurityConstant;
import com.example.util.JwtUtil;
import com.example.util.RedisUtil;
import com.example.util.ResponseUtil;
import com.example.vo.Result;
import com.example.vo.ResultCode;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Jwt 权限认证过滤器
 *
 * @author 朽
 */
@Slf4j
@Component
@AllArgsConstructor
public class SecurityJwtAuthenticationFilter extends OncePerRequestFilter {
    private JwtUtil jwtUtil;
    private ObjectMapper objectMapper;
    private RedisUtil redisUtil;
    private AntPathMatcher antPathMatcher;

    //不是无条件放行的路径，需要根据登录/未登录进行不同的操作
    private final Set<String> specialPath = new HashSet<>();
    {
        specialPath.add("/buyer/comment/getLikeIds");
        specialPath.add("/buyer/collect/collectSpuStatus");
        specialPath.add("/buyer/collect/collectStoreStatus");
        specialPath.add("/buyer/recommend/spulist");
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 1、如果是登录页则跳过
        if (SecurityConfig.LOGIN_URI.equalsIgnoreCase(request.getRequestURI()) ||
                SecurityConfig.SmsCodeLogin_URI.equalsIgnoreCase(request.getRequestURI()) ||
                Arrays.stream(SecurityConfig.WHITE_LIST).anyMatch(white -> {
                    return antPathMatcher.match(white, request.getRequestURI());
                })
        ) {
            filterChain.doFilter(request, response);
            return;
        }

        // 2、在jwt认证之前有没有其他方式认证过
        Authentication otherAuthentication = SecurityContextHolder.getContext().getAuthentication();
        if (otherAuthentication != null && otherAuthentication.isAuthenticated()) {
            filterChain.doFilter(request, response);
            return;
        }
        // 3、获取请求头中的jwt
        String jwt = request.getHeader(SecurityConstant.TOKEN_HEADER);
        try {
            // 4、获取redis中存储的信息
            String subject = jwtUtil.parse(jwt).getSubject();

            String key = RedisConstance.LOGIN_USER_TOKEN + subject;

            Object o = redisUtil.get(key);
            // 构建Authentication对象
            LoginUser loginUser = JSON.parseObject(o.toString(), LoginUser.class);
            if (loginUser == null) {
                //当未登录时，且又属于特殊路径，需要被放行
                if(specialPath.contains(request.getRequestURI())){
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("", "",
                            List.of(new SimpleGrantedAuthority(request.getRequestURI())));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                    filterChain.doFilter(request,response);
                }else{
                    ResponseUtil.writeJson(response, objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0311)));
                }
                return;
            }
            //将登录用户信息封装到SpringSecurity
            Authentication authentication = (Authentication) redisUtil.hashGet(SecurityConstant.TOKEN_KEY, subject);
            // 5、构建出Authentication对象，并且放到SecurityContext，表示认证成功
            SecurityContextHolder.getContext().setAuthentication(authentication);
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            if(specialPath.contains(request.getRequestURI())){
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken("", "",
                        List.of(new SimpleGrantedAuthority(request.getRequestURI())));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                filterChain.doFilter(request,response);
            }else{
                ResponseUtil.writeJson(response, objectMapper.writeValueAsString(Result.error(ResultCode.CLIENT_A0304)));
            }
        }
    }

}
